// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
 * Author: Huawei OS Kernel Lab
 * Create: Thu Aug 15 16:14:23 2023
 */

#include <linux/pfn.h>
#include <linux/mman.h>
#include <linux/module.h>
#include <linux/highmem.h>
#include <linux/memblock.h>
#include <liblinux/pal.h>

extern pte_t *pkmap_page_table;

int __weak arch_highmem_page_mapped(struct page *page) { return 0; }

void *kmap_atomic_high_prot(struct page *page, pgprot_t prot)
{
	unsigned long vaddr;

	if (arch_highmem_page_mapped(page))
		return page_to_virt(page);

	vaddr = (unsigned long)kmap_high(page);
	if (vaddr >= PKMAP_ADDR(0) && vaddr < PKMAP_ADDR(LAST_PKMAP)) {
		if (liblinux_pal_vm_mmap(page_to_pfn(page) << PAGE_SHIFT, vaddr,
		    PAGE_SIZE, PROT_READ | PROT_WRITE, LIBLINUX_PAL_REMAP_CACHE) < 0) {
			panic("%s: HIGHMEM map failed raddr: %lx\n", __func__, vaddr);
		}
	}

	return (void *)vaddr;
}
EXPORT_SYMBOL(kmap_atomic_high_prot);

void kunmap_atomic_high(void *kvaddr)
{
	unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;

	if (arch_highmem_page_mapped(virt_to_page((void *)vaddr)))
		return;

	if (vaddr >= PKMAP_ADDR(0) && vaddr < PKMAP_ADDR(LAST_PKMAP)) {
		liblinux_pal_vm_zap((unsigned long)vaddr, PAGE_SIZE, 0);
		kunmap_high(pte_page(pkmap_page_table[PKMAP_NR(vaddr)]));
	}
}
EXPORT_SYMBOL(kunmap_atomic_high);

void __init kmap_init(void)
{
	pkmap_page_table = (pte_t *)memblock_alloc_low(LAST_PKMAP * sizeof(pte_t), PAGE_SIZE);
}

unsigned long highmem_kmap_base;
